#ifndef QUOTIENT_H
#define QUOTIENT_H

class Quotient
{
	public:
		Quotient(double aNumber)
		{
			bool bBusy = true;
			long lPrecision = 1;
			lPrecision <<= 30;
			lPrecision -= 1;
			long iIteration = lPrecision;
			long iNumHigh = lPrecision;
			long iNumLow = 0;
			long itry = 0;
			while(bBusy)
			{
				++itry;
				long iNum = ((iIteration + 1) / 2);
				long iDen = lPrecision;
				float fCalc = (float)iNum / (float)iDen;

				if( fCalc > aNumber ) {iNumHigh = iNum;}
				else if( fCalc < aNumber ) {iNumLow = iNum;}
				else {mNumerator = iNum; mDenominator = iDen; bBusy = false;}  

				iIteration = (iNumHigh + iNumLow);

				if( iNumHigh - iNumLow == 1 )
				{
					float fHighError = ((float)iNumHigh / (float)iDen) - aNumber;
					float fLowError = aNumber - ((float)iNumLow / (float)iDen);
					if( fHighError > fLowError ) {mNumerator = iNumLow;}
					else {mNumerator = iNumHigh;}
					mDenominator = iDen;	
					bBusy = false;
				}
			}
			ReducedForm();
		}
		Quotient(long aNumerator, long aDenominator)
			:mNumerator(aNumerator)
			,mDenominator(aDenominator)
			{ReducedForm();}
		Quotient(const Quotient& aQuotient)
			:mNumerator(aQuotient.mNumerator)
			,mDenominator(aQuotient.mDenominator)
		{}
		const Quotient& operator=(const Quotient& Right)
		{
			if( this != &Right )
			{
				mNumerator = Right.mNumerator;
				mDenominator = Right.mDenominator;
			}
			return *this;
		}
		~Quotient(){}

		bool operator==(const Quotient& Right)
		{
			if( this == &Right ) {return true;}
			else
			{
				return (Right.mNumerator == mNumerator &&
					Right.mDenominator == mDenominator);
			}
		}
		bool operator!=(const Quotient& Right)
			{return ( ! operator==(Right) );}

		Quotient operator+ (long Right)
		{
			return Quotient(mNumerator + Right * mDenominator, mDenominator);
		}
		Quotient& operator+= (long Right)
		{
			mNumerator += Right * mDenominator;
			return *this;
		}
		Quotient operator+ (const Quotient& Right)
		{
			long gcd( GCD(mDenominator, Right.mDenominator) );
			if( gcd > 1 )
			{
				long Multi1( Right.mDenominator / gcd);
				long Multi2( mDenominator / gcd);
				return Quotient( mNumerator * Multi1 + Right.mNumerator * Multi2, mDenominator * Multi1);
			}
			else //gcd == 1
			{
				return Quotient(
						mNumerator * Right.mDenominator +
						Right.mNumerator * mDenominator,
						mDenominator * Right.mDenominator);
			}
		}
		Quotient& operator+= (const Quotient& Right)
		{
			long gcd( GCD(mDenominator, Right.mDenominator) );
			if( gcd > 1 )
			{
				long Multi1( Right.mDenominator / gcd);
				long Multi2( mDenominator / gcd);
				mNumerator =  mNumerator * Multi1 + Right.mNumerator * Multi2;
				mDenominator = mDenominator * Multi1;
			}
			else //gcd == 1
			{
				mNumerator = mNumerator * Right.mDenominator + Right.mNumerator * mDenominator;
				mDenominator = mDenominator * Right.mDenominator;
			}
			ReducedForm();
			return *this;
		}

		Quotient operator- (long Right)
		{
			return Quotient(mNumerator - Right * mDenominator, mDenominator);
		}
		Quotient& operator-= (long Right)
		{
			mNumerator -= Right * mDenominator;
			return *this;
		}
		Quotient operator- (const Quotient& Right)
		{
			long gcd( GCD(mDenominator, Right.mDenominator) );
			if( gcd > 1 )
			{
				long Multi1( Right.mDenominator / gcd);
				long Multi2( mDenominator / gcd);
				return Quotient( mNumerator * Multi1 - Right.mNumerator * Multi2, mDenominator * Multi1);
			}
			else //gcd == 1
			{
				return Quotient(
						mNumerator * Right.mDenominator -
						Right.mNumerator * mDenominator,
						mDenominator * Right.mDenominator);
			}
		}
		Quotient& operator-= (const Quotient& Right)
		{
			long gcd( GCD(mDenominator, Right.mDenominator) );
			if( gcd > 1 )
			{
				long Multi1( Right.mDenominator / gcd);
				long Multi2( mDenominator / gcd);
				mNumerator =  mNumerator * Multi1 - Right.mNumerator * Multi2;
				mDenominator = mDenominator * Multi1;
			}
			else //gcd == 1
			{
				mNumerator = mNumerator * Right.mDenominator - Right.mNumerator * mDenominator;
				mDenominator = mDenominator * Right.mDenominator;
			}
			ReducedForm();
			return *this;
		}

		Quotient operator* (long Right)
		{
			return Quotient(mNumerator * Right, mDenominator);
		}
		Quotient& operator*= (long Right)
		{
			mNumerator *= Right;
			ReducedForm();
			return *this;
		}
		Quotient operator* (const Quotient& Right)
		{
			return Quotient(mNumerator * Right.mNumerator,
					mDenominator * Right.mDenominator);
		}
		const Quotient& operator*= (const Quotient& Right)
		{
			mNumerator *= Right.mNumerator;
			mDenominator *= Right.mDenominator;
			ReducedForm();
			return *this;
		}

		Quotient operator/ (long Right)
		{
			long lcm( LCM(mNumerator, Right));
			if( lcm != mNumerator )
			{
				return Quotient(lcm / Right, mDenominator * (lcm / mNumerator));
			}
			else
			{
				return Quotient(mNumerator / Right, mDenominator);
			}
		}
		Quotient& operator/= (long Right)
		{
			long lcm( LCM(mNumerator, Right));
			if( lcm != mNumerator )
			{
				mNumerator = lcm / Right;
				mDenominator *= lcm / mNumerator;
			}
			else
			{
				mNumerator = mNumerator / Right;
			}
			ReducedForm();
			return *this;
		}
		Quotient operator/ (const Quotient& Right)
		{
			return operator*(Quotient(Right.mDenominator, Right.mNumerator));
		}
		const Quotient& operator/= (const Quotient& Right)
		{
			return operator*=(Quotient(Right.mDenominator, Right.mNumerator));
		}

		long Numerator(void) {return mNumerator;}
		long Denominator(void) {return mDenominator;}
	protected:
	private:
		Quotient();
		
		void ReducedForm(void)
		{
			long gcd( GCD(mNumerator, mDenominator) );
			if( gcd > 1 );
			{
				mNumerator /= gcd;
				mDenominator /= gcd;
			}
		}
		//Greatest Common Divisor
		long GCD(long aNumber1, long aNumber2)
		{
			if( aNumber1 < 0 ) aNumber1 = -aNumber1;
			if( aNumber2 < 0 ) aNumber2 = -aNumber2;
			while( aNumber2 != 0 )
			{
				long Remainder( aNumber1 % aNumber2 );
				aNumber1 = aNumber2;
				aNumber2 = Remainder;
			}
			return aNumber1;
		}
		//Least Common Multiple
		long LCM(long aNumber1, long aNumber2)
		{
			if( aNumber1 < 0 ) aNumber1 = -aNumber1;
			if( aNumber2 < 0 ) aNumber2 = -aNumber2;

			long gcd( GCD(aNumber1, aNumber2) );
			return ( (aNumber1 / gcd) * aNumber2 );
		}

		long mNumerator;
		long mDenominator;
};
#endif //QUOTIENT_H
